package mth.masterimp;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import java.util.List;

import rs.fon.whibo.GC.component.DistanceMeasure.*;
import rs.fon.whibo.problem.SubproblemParameter;

import com.rapidminer.datatable.*;

import mth.masterimp.Tools;

public class DataRepresentator {
	/*
	 * Diese Klasse dient der Repräsentation der Daten. Hier erfolgt sowohl der Aufbau der 
	 * Werte- als auch der Distanzmatrix. Weiterhin existiert eine Methode um Gewichtungen 
	 * auf die Eingabedaten anzuwenden.
	 */
	private static SimpleDataTable valueMatrix;
	private static double[][] distanceMatrix;
	private static int k;
	private static String distMeasure;
	
	public DataRepresentator() {
		//do nothing
		//everything is static, so you can access all actual values
		//the data representation should be unique
	}
	
	DataRepresentator(String file, String distM, int knew) {
		k=knew;
		distMeasure = distM;
		BufferedReader lineRider;
		try {
			lineRider = new BufferedReader(new FileReader(file));
			//it is supposed, that the first line contains names for variables
			String element=lineRider.readLine();
			if (element!=null) {
				String[] selem = element.split(",");
				valueMatrix = new SimpleDataTable("data", selem);
				for (int a=0; a<selem.length; a++) {
					if (selem[a].equals("id") || selem[a].equals("label") || selem[a].equals("cluster"))
						valueMatrix.setSpecial(a, true);
				}
			}
			element=lineRider.readLine();
			
			while (element!=null) {
				double[] line=new double[element.split(",").length]; 
				for (int a=0; a<element.split(",").length; a++) {
					line[a] = Double.parseDouble(element.split(",")[a]);
				}
				addRow(line);
				element=lineRider.readLine();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			calculateDistances(distM);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void addRow(double[] line) {
		SimpleDataTableRow r = new SimpleDataTableRow(line);
		valueMatrix.add(r);
	}
	
	public void calculateDistances(String measure) throws Exception {
		DistanceMeasure d = generateDistanceMeasure(measure,null);
		fillDistanceMatrix(d);
	}
	
	public DistanceMeasure generateDistanceMeasure(String measure, List<SubproblemParameter> spp) throws Exception {
		DistanceMeasure d;
		if (measure.equals("Camberra")) {
			d = new Camberra(spp);
		} else if (measure.equals("Chebychev")) {
			d = new Chebychev(spp);
		} else if (measure.equals("CityBlok")) {
			d = new CityBlok(spp);
		} else if (measure.equals("CorrelationNumerical")) {
			d = new CorrelationNumerical(spp);
		} else if (measure.equals("CosineSimilarity")) {
			d = new CosineSimilarity(spp);
		} else if (measure.equals("DiceNumerical")) {
			d = new DiceNumerical(spp);
		} else if (measure.equals("DTWNumerical")) {
			d = new DTWNumerical(spp);
		} else if (measure.equals("Euclidian")) {
			d = new Euclidian(spp);
		} else if (measure.equals("InnerProduct")) {
			d = new InnerProduct(spp);
		} else if (measure.equals("JaccardNumerical")) {
			d = new JaccardNumerical(spp);
		} else if (measure.equals("MaxProduct")) {
			d = new MaxProduct(spp);
		} else if (measure.equals("OverlapNumerical")) {
			d = new OverlapNumerical(spp);
		} else {
			throw new Exception("Distanzmaß existiert nicht");
		}
		return d;
	}
	
	private void fillDistanceMatrix(DistanceMeasure disMes) {
		//prepare the distanceMatrix (it has no initial size yet)
		distanceMatrix = new double[valueMatrix.getNumberOfRows()][valueMatrix.getNumberOfRows()];
		//fill the distance matrix
		double[] a_element= new double[valueMatrix.getNumberOfColumns()-valueMatrix.getNumberOfSpecialColumns()];
		double[] b_element= new double[valueMatrix.getNumberOfColumns()-valueMatrix.getNumberOfSpecialColumns()];
		
		for (int a=0; a<valueMatrix.getNumberOfRows();a++) {
			for (int b=0; b<valueMatrix.getNumberOfRows(); b++) {
				for (int c=0; c<valueMatrix.getNumberOfColumns()-valueMatrix.getNumberOfSpecialColumns(); c++) {
					a_element[c]=Tools.extractDoubleArrayFromDataRow(valueMatrix.getRow(a))[c];
					b_element[c]=Tools.extractDoubleArrayFromDataRow(valueMatrix.getRow(b))[c];
				}
				distanceMatrix[a][b]=disMes.calculateDistance(a_element, b_element);
              
//				PrintWriter out = null;
//              OutputStream outStream = null;
//              try {
//                              outStream = new FileOutputStream(new File("./df.df"), false);
//                      out = new PrintWriter(new OutputStreamWriter(outStream, Charset.defaultCharset()));
//                      for (int i=0; i<distanceMatrix.length;i++) {
//                    	  for (int j=0; j<distanceMatrix[i].length; j++) {
//                              out.print(distanceMatrix[i][j]);
//                    	  }
//                    	  out.println();
//                      }
////     
//              } catch (IOException e) {
//                      try {
//						throw e;
//					} catch (IOException e1) {
//						e1.printStackTrace();
//					}
//              } finally {
//                      if (out != null) {
//                              out.close();            
//                      }
//                      if (outStream != null) {
//                              try {
//								outStream.close();
//							} catch (IOException e) {
//								e.printStackTrace();
//							}                              
//                      }
//              }
			}
		}
	}
	
	public double[][] getDistanceMatrix() {
		return distanceMatrix;
	}
	
	public double[] getDistanceMatrixRow (int r) {
		return distanceMatrix[r];
	}
	
	public String getDistanceMeasure() {
		return distMeasure;
	}
	
	public int getK() {
		return k;
	}
	
	public int getNearestElementTo(int id) {
		double minDist=distanceMatrix[id][0];
		int minDistElem=0;
		for (int a=0; a<distanceMatrix[id].length; a++) {
			if (a!=id) {
				if (distanceMatrix[id][a]<minDist) {
					minDist=distanceMatrix[id][a];
					minDistElem=a;
				}
			}
		}
		return minDistElem;
	}
	
	public SimpleDataTable getValueMatrix() {
		return valueMatrix;
	}
	
	public void print(String file) {
//		printDistanceMatrix(file);
		printValueMatrix(file);
	}
	
	public void printDistanceMatrix(String file) {
		String outString="";
		outString+="Eigenschaften der Distanzmatrix\n";
		outString+="Distanzmass: "+distMeasure+"\n";
		outString+="Anzahl Spalten:" + distanceMatrix.length + "\n";
		outString+="Anzahl Zeilen:" + distanceMatrix[0].length + "\n";
		try {
			FileWriter fw = new  FileWriter(file+"_distanceMatrix_"+distMeasure+"_prop", false);
			fw.write(outString);
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		outString="";
		for (int a=0; a<distanceMatrix.length; a++) {
			for (int b=0; b<distanceMatrix[a].length; b++) {
				if (b==distanceMatrix[a].length-1) {
					outString+=distanceMatrix[a][b];
				} else {
					outString+=distanceMatrix[a][b]+",";
				}
			}
			outString+="\n";
		}
		try {
			FileWriter fw = new  FileWriter(file+"_distanceMatrix_"+distMeasure, false);
			fw.write(outString);
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void printValueMatrix(String file) {
		String outString="";
//		outString+="Eigenschaften der Wertematrix\n";
//		outString+="Attribute insgesamt: "+getValueMatrix().getNumberOfColumns() + "\n";
//		outString+="spezielle Attribute: "+getValueMatrix().getNumberOfSpecialColumns() + "\n";
//		outString+="Attribute mit Werten: "+(getValueMatrix().getNumberOfColumns()-getValueMatrix().getNumberOfSpecialColumns()) + "\n\n";
//		outString+="Anzahl Zeilen: "+getValueMatrix().getNumberOfRows();
//		try {
//			FileWriter fw = new  FileWriter(file+"_ValueMatrix_prop", true);
//			fw.write(outString);
//			fw.close();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//		
//		outString="";
		for (int a=0; a<valueMatrix.getColumnNames().length; a++) {
			if (a==valueMatrix.getColumnNames().length-1) {
				outString+=valueMatrix.getColumnNames()[a]+"\n";
			} else {
				outString+=valueMatrix.getColumnNames()[a]+",";
			}
		}
		for (int a=0; a<valueMatrix.getNumberOfRows(); a++) {
			for (int b=0; b<valueMatrix.getNumberOfColumns(); b++) {
				if (b==valueMatrix.getNumberOfColumns()-1) {
					outString+=valueMatrix.getCell(a, b);
				} else {
					outString+=valueMatrix.getCell(a, b)+",";
				}
			}
			outString+="\n";
		}
		try {
			FileWriter fw = new  FileWriter(file+"_valueMatrix_allColumns", false);
			fw.write(outString);
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		outString="";
		for (int a=0; a<valueMatrix.getNumberOfRows(); a++) {
			for (int b=0; b<valueMatrix.getNumberOfColumns()-valueMatrix.getNumberOfSpecialColumns(); b++) {
				if (b==valueMatrix.getNumberOfColumns()-valueMatrix.getNumberOfSpecialColumns()-1) {
					outString+=valueMatrix.getCell(a, b);
				} else {
					outString+=valueMatrix.getCell(a, b)+",";
				}
			}
			outString+="\n";
		}
		try {
			FileWriter fw = new  FileWriter(file+"_valueMatrix_ValueColumns", false);
			fw.write(outString);
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void updateValueMatrix(double[] weights) {
		double[][]	tempValueMatrix=new double[valueMatrix.getNumberOfRows()][valueMatrix.getNumberOfColumns()];
		for (int a=0; a<valueMatrix.getNumberOfRows(); a++) {
			for (int b=0; b<valueMatrix.getNumberOfColumns(); b++) {
				if (b<weights.length) {
					tempValueMatrix[a][b]=Double.parseDouble(valueMatrix.getCell(a, b))*weights[b];
				} else {
					tempValueMatrix[a][b]=Double.parseDouble(valueMatrix.getCell(a,b));
				}
			}
		}
		for (int a=valueMatrix.getNumberOfRows()-1; a>=0; a--) {
			valueMatrix.remove(valueMatrix.getRow(a));
		}
		for (int a=0; a<tempValueMatrix.length; a++) {
			addRow(tempValueMatrix[a]);
		}
	}
}
